package test.my.com.myapplicationtest.roll_number;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;

import java.util.ArrayList;
import java.util.List;

/**
 * Author : Cooper
 * Time : 2017/2/6  15:37
 * Description : 数字翻滚view
 */
public class ScrollNumber extends View {

    /**
     * 当前画布显示的数字
     */
    private int mCurNum;
    /**
     * 要显示的目标数字
     */
    private int mTargetNum;
    /**
     * 将要滚动显示的下一个数字
     */
    private int mNextNum;

    /**
     * 数字之间滚动的偏移量，负数则表示向上偏移
     */
    private float mOffset;

    /**
     * 用于显示的数字
     */
    private int[] numArray = {0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9};
    /**
     * 用于本次从mCurNum到mTargetNum之间显示的数字集合
     */
    private List<Integer> showNumListTemp = new ArrayList<>();
    // 动画总时间
    private int totalDuration = 5000;
    /**
     * 记录初始值和目标值之间的个数，用于计算数字之间滚动所需要的时间，差值越大，每个数字滚动所需要的时间越短
     * 例如1至2，滚动时间为500/1;2至9，滚动时间为500/7
     */
    private int durationBase = 0;
    /**
     * 获取本次showNumListTemp中的数字，用来显示
     */
    private int countNum = 0;

    private Paint mPaint;
    private Context mContext;
    private Interpolator mInterpolator = new AccelerateDecelerateInterpolator();


    private int mTextCenterX;
    private int mTextHeight;
    private Rect mTextBounds = new Rect();
    private float mTextSize = sp2px(200);
    private int mTextColor = 0xFF000000;
    private Typeface mTypeface;

    private ValueAnimator valueAnimator;

    public ScrollNumber(Context context) {
        this(context, null);
    }

    public ScrollNumber(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ScrollNumber(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        mContext = context;

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setTextAlign(Paint.Align.CENTER);
        mPaint.setTextSize(mTextSize);
        mPaint.setColor(mTextColor);

        if (mTypeface != null) mPaint.setTypeface(mTypeface);
        measureTextHeight();

    }

    @Override
    protected void onDraw(Canvas canvas) {

        canvas.translate(0, mOffset * getMeasuredHeight());
        drawSelf(canvas);
        drawNext(canvas);
        Log.e("ah","mOffset * getMeasuredHeight() ==== "+mOffset * getMeasuredHeight());
        Log.e("ah","mOffset +++++ "+mOffset);
        Log.e("ah","getMeasuredHeight() @@@@@ "+getMeasuredHeight());
    }

    private void drawNext(Canvas canvas) {
        int y = getMeasuredHeight() * 3 / 2;
        canvas.drawText(mNextNum + "", mTextCenterX, y + mTextHeight / 2,
                mPaint);
    }

    private void drawSelf(Canvas canvas) {
        int y = getMeasuredHeight() / 2;
        canvas.drawText(mCurNum + "", mTextCenterX, y + mTextHeight / 2, mPaint);
    }

    /**
     * 添加滚动动画，并且计算偏移量
     */
    private void caculateOffet() {
        valueAnimator = ValueAnimator.ofFloat(0,1);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.setDuration(totalDuration/durationBase);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 偏移量为负数，表明要向上滚动
                mOffset = -(float)animation.getAnimatedValue();
                invalidate();
            }
        });
        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                // 当滚动一个数字后，要将滚动后的数字赋值给mCurNum
                mCurNum = mNextNum;
                // 当滚动的下一个数字和最终数字相同时，本次滚动动画结束，否则继续滚动
                if (mNextNum == mTargetNum) {
                    return;
                }
                caculateNext();
                caculateOffet();
            }
        });
        valueAnimator.start();
    }

    /**
     * 设置要滚动显示的数字
     * @param curNum 从哪个数字开始滚动
     * @param targetNum 将要滚动到的最终数字
     */
    public void setNumber(int curNum,int targetNum) {
        if (curNum == targetNum) {
            return;
        }
        mCurNum = curNum;
        showNumListTemp.clear();
        durationBase = 0;
        countNum = 0;
        this.mTargetNum = targetNum;
        for (int i=mCurNum+1;i<numArray.length;i++) {
            showNumListTemp.add(numArray[i]);
            durationBase++;
            if (targetNum == numArray[i]) {
                break;
            }
        }
        caculateNext();
        caculateOffet();
    }

    /**
     * 获取下一个要滚动显示的数字
     */
    private void caculateNext() {
        mNextNum = showNumListTemp.get(countNum);
        if (countNum < durationBase) {
            countNum++;
        }
    }

    private void measureTextHeight() {
        mPaint.getTextBounds(mCurNum + "", 0, 1, mTextBounds);
        mTextHeight = mTextBounds.height();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = measureWidth(widthMeasureSpec);
        int height = measureHeight(heightMeasureSpec);
        setMeasuredDimension(width, height);

        mTextCenterX = (getMeasuredWidth() - getPaddingLeft() - getPaddingRight()) / 2;
    }

    private int measureHeight(int measureSpec) {
        int mode = MeasureSpec.getMode(measureSpec);
        int val = MeasureSpec.getSize(measureSpec);
        int result = 0;
        switch (mode) {
            case MeasureSpec.EXACTLY:
                result = val;
                break;
            case MeasureSpec.AT_MOST:
            case MeasureSpec.UNSPECIFIED:
                mPaint.getTextBounds("0", 0, 1, mTextBounds);
                result = mTextBounds.height();
                break;
        }
        result = mode == MeasureSpec.AT_MOST ? Math.min(result, val) : result;
        return result + getPaddingTop() + getPaddingBottom()+dp2px(1);
    }

    private int measureWidth(int measureSpec) {
        int mode = MeasureSpec.getMode(measureSpec);
        int val = MeasureSpec.getSize(measureSpec);
        int result = 0;
        switch (mode) {
            case MeasureSpec.EXACTLY:
                result = val;
                break;
            case MeasureSpec.AT_MOST:
            case MeasureSpec.UNSPECIFIED:
                mPaint.getTextBounds("0", 0, 1, mTextBounds);
                result = mTextBounds.width();
                break;
        }
        result = mode == MeasureSpec.AT_MOST ? Math.min(result, val) : result;
        return result + getPaddingLeft() + getPaddingRight() /*+ 5*/;
    }

    public void setTextSize(float textSize) {
        this.mTextSize = textSize;
        mPaint.setTextSize(mTextSize);
        measureTextHeight();
        requestLayout();
        invalidate();
    }


    public void setTextFont(String fileName) {
        if (TextUtils.isEmpty(fileName))
            throw new IllegalArgumentException("please check file name end with '.ttf' or '.otf'");
        mTypeface = Typeface.createFromAsset(mContext.getAssets(), fileName);
        if (mTypeface == null) throw new RuntimeException("please check your font!");
        mPaint.setTypeface(mTypeface);
        requestLayout();
        invalidate();
    }

    public void setTextColor(int mTextColor) {
        this.mTextColor = mTextColor;
        mPaint.setColor(mTextColor);
        invalidate();
    }

    public void setInterpolator(Interpolator interpolator) {
        mInterpolator = interpolator;
    }

    private int dp2px(float dpVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                dpVal, getResources().getDisplayMetrics());
    }

    private int sp2px(float dpVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                dpVal, getResources().getDisplayMetrics());
    }


}

